package be.kuleuven.cs.mop.app.parser;


import static be.kuleuven.cs.mop.app.parser.ParserUtils.getNodes;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.SortedMap;
import java.util.TreeMap;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;

import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Theme;
import be.kuleuven.cs.mop.domain.model.impl.FieldType;
import be.kuleuven.cs.mop.domain.model.impl.Interval;
import be.kuleuven.cs.mop.domain.model.impl.ResourceTypeImpl;
import be.kuleuven.cs.mop.domain.model.impl.TaskTypeImpl;
import be.kuleuven.cs.mop.domain.model.impl.ThemeImpl;
import be.kuleuven.cs.mop.domain.model.impl.UserTypeImpl;
import be.kuleuven.cs.mop.domain.model.impl.World;



/**
 * XML parser for the custom theme configuration file
 */
public class ThemeParser {
	
	public static final void main(final String... args) {
		//	TODO:	Remove on release
		
		try {
			final World world = new World();
			new ThemeParser().parse(new File("data/themes.xml"), world);
			
			System.out.println("USER TYPES:");
			for (final UserTypeImpl user : world.getUserTypes())
				System.out.println("    " + user.getName());
			
			System.out.println("RESOURCE TYPES:");
			for (final ResourceTypeImpl resource : world.getResourceTypes())
				System.out.println("    " + resource.getName());
			
			System.out.println("TASK TYPES:");
			for (final TaskTypeImpl task : world.getTaskTypes())
				System.out.println("    " + task.getName());
		} catch (final Exception e) {
			e.printStackTrace();
		}
	}
	
	
	
	
	
	private FieldType getFieldType(final String type) {
		if ((type == null) || type.isEmpty()) {
			throw new IllegalArgumentException("Invalid FieldType:  " + type);
		} else if (type.equalsIgnoreCase("numeric")) {
			return FieldType.NUMERICAL;
		} else if (type.equalsIgnoreCase("textual")) {
			return FieldType.TEXTUAL;
		}
		
		throw new IllegalArgumentException("Invalid FieldType:  " + type);
	}
	
	private Interval getInterval(final Element node) throws TaskManagerException {
		try {
			final int min = Integer.parseInt(node.getAttributeValue("min"));
			try {
				return new Interval(min,
						Integer.parseInt(node.getAttributeValue("max")));
			} catch (final NumberFormatException e) {
				return new Interval(min, false);
			}
		} catch (final NumberFormatException e) {
			throw new IllegalArgumentException("Invalid XML syntax");
		}
	}
	
	private Object getRequirement(final ThemeImpl theme, final String id) {
		try {
			return theme.getResourceType(id);
		} catch (final RuntimeException e) {
			//	<code>id</code> represents a UserType
		}
		
		try {
			return theme.getUserType(id);
		} catch (final RuntimeException e) {
			//	<code>id</code> represents a ResourceType
		}
		
		throw new RuntimeException("Unknown ID:  " + id);
	}
	
	public Theme parse(final File file, final World world)
			throws IOException, JDOMException, TaskManagerException {
		if (file == null)
			throw new NullPointerException("File == NULL");
		if (world == null)
			throw new NullPointerException("World == NULL");
		
		final Document	doc		= ParserUtils.parse(file);
		final Element	root	= doc.getRootElement();
		if (!root.getName().equalsIgnoreCase("Theme"))
			throw new IOException("Invalid XML syntax");
		
		final ThemeImpl theme = new ThemeImpl();
		
		//	User types
		for (final Element node : getNodes(root, "UserTypes"))
			parseUserType(theme, world, node);
		
		//	Resource types
		for (final Element node : getNodes(root, "ResourceTypes"))
			parseResourceType(theme, world, node);
		
		//	Task types
		for (final Element node : getNodes(root, "TaskTypes"))
			parseTaskType(theme, world, node);
		
		return theme;
	}
	
	private void parseResourceType(final ThemeImpl theme,
			final World world, final Element node) throws TaskManagerException {
		theme.setResourceType(node.getAttributeValue("id"),
				new ResourceTypeImpl(world, node.getAttributeValue("name")));
	}
	
	private void parseTaskType(final ThemeImpl theme,
			final World world, final Element node) throws TaskManagerException {
		//	Fields
		final HashMap<String, FieldType>	fields	= new HashMap<String, FieldType>();
		final SortedMap<String, String>		ids		= new TreeMap<String, String>();
		for (final Element field : getNodes(node, "fields")) {
			final String name = field.getAttributeValue("name");
			fields.put(name, getFieldType(field.getAttributeValue("nature")));
			ids.put(field.getAttributeValue("id"), name);
		}
		
		
		//	Requirements
		final HashMap<UserTypeImpl, Interval>		helpers		=
			new HashMap<UserTypeImpl, Interval>();
		final HashMap<ResourceTypeImpl, Interval>	resources	=
			new HashMap<ResourceTypeImpl, Interval>();
		
		for (final Element req : getNodes(node, "requires")) {
			final Interval	interval	= getInterval(req);
			final Object	requirement =
				getRequirement(theme, req.getAttributeValue("type"));
			
			if (requirement == null) {
				throw new RuntimeException("Missing data");
			} else if (requirement instanceof ResourceTypeImpl) {
				resources.put((ResourceTypeImpl)requirement, interval);
			} else if (requirement instanceof UserTypeImpl) {
				helpers.put((UserTypeImpl)requirement, interval);
			}
		}
		
		
		//	Owners
		final HashSet<UserTypeImpl> owners = new HashSet<UserTypeImpl>();
		for (final Element owner : getNodes(node, "owners")) {
			try {
				owners.add(theme.getUserType(owner.getAttributeValue("type")));
			} catch (final Exception e) {
				throw new RuntimeException("Invalid XML");
			}
		}
		
		
		final TaskTypeImpl type =
			new TaskTypeImpl(world, node.getAttributeValue("name"),
				owners, helpers, fields, resources);
		theme.setFieldNames(type, ids);
		theme.setTaskType(node.getAttributeValue("id"), type);
	}
	
	private void parseUserType(final ThemeImpl theme,
			final World world, final Element node) throws TaskManagerException {
		theme.setUserType(node.getAttributeValue("id"),
				new UserTypeImpl(world, node.getAttributeValue("name")));
	}
	
}